package handler

import (
	"crypto"
	_ "crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"

	"gitcode.net/togolife/nfttoken/common"
	"gitcode.net/togolife/nfttoken/trade"
)

type ApprovalInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	CallAddr     string `json:"callAddr"`
	ApprovedAddr string `json:"approvedAddr"`
	TokenId      string `json:"tokenId"`
	Sign         string `json:"sign"`
}

type ApprovalOutput struct {
	RetCode int    `json:"retCode"`
	RetMsg  string `json:"retMsg"`
	TxHash  string `json:"txHash,omitempty"`
}

func ApprovalRet(w http.ResponseWriter, out *ApprovalOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setApprovalOutput(out *ApprovalOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *ApprovalInput) checkSign() bool {
	s := "approvedAddr=" + input.ApprovedAddr
	s += "&callAddr=" + input.CallAddr
	s += "&contractAddr=" + input.ContractAddr
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	s += "&tokenId=" + input.TokenId
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func ApprovalIf(w http.ResponseWriter, req *http.Request) {
	input := ApprovalInput{}
	output := ApprovalOutput{}
	defer ApprovalRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read approval http request body failed! [%v]", err)
		setApprovalOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode approval http request body failed! [%v]", err)
		setApprovalOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setApprovalOutput(&output, common.NewError(401))
		return
	}
	tx, nftErr := trade.Approve(input.ReqChain, input.ContractAddr, input.CallAddr, input.ApprovedAddr, input.TokenId)
	if !common.Valid(nftErr) {
		hdLog.LogE("Approve failed [%v]", nftErr.ErrorMsg)
		setApprovalOutput(&output, nftErr)
		return
	}
	setApprovalOutput(&output, common.NewError(200))
	output.TxHash = tx
	return
}

type ApprovalForAllInput struct {
	ReqChain     string `json:"reqChain"`
	ContractAddr string `json:"contractAddr"`
	CallAddr     string `json:"callAddr"`
	ApprovedAddr string `json:"approvedAddr"`
	Flag         bool   `json:"flag"`
	Sign         string `json:"sign"`
}

type ApprovalForAllOutput struct {
	RetCode int    `json:"retCode"`
	RetMsg  string `json:"retMsg"`
	TxHash  string `json:"txHash,omitempty"`
}

func ApprovalForAllRet(w http.ResponseWriter, out *ApprovalForAllOutput) {
	v, err := json.Marshal(out)
	if err != nil {
		return
	}
	w.Write(v)
}

func setApprovalForAllOutput(out *ApprovalForAllOutput, err common.NftError) {
	out.RetCode = err.ErrorNo
	out.RetMsg = err.ErrorMsg
}

func (input *ApprovalForAllInput) checkSign() bool {
	s := "approvedAddr=" + input.ApprovedAddr
	s += "&callAddr=" + input.CallAddr
	s += "&contractAddr=" + input.ContractAddr
	s += "&flag=" + strconv.FormatBool(input.Flag)
	s += "&reqChain=" + input.ReqChain
	s += "&sign=" + hdConf.SignRandomKey
	m := crypto.SHA256.New()
	m.Write([]byte(s))
	v := hex.EncodeToString(m.Sum(nil))
	return v == input.Sign
}

func ApprovalForAllIf(w http.ResponseWriter, req *http.Request) {
	input := ApprovalForAllInput{}
	output := ApprovalForAllOutput{}
	defer ApprovalForAllRet(w, &output)
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		hdLog.LogE("Read approval for all http request body failed! [%v]", err)
		setApprovalForAllOutput(&output, common.NewError(400))
		return
	}
	err = json.Unmarshal(body, &input)
	if err != nil {
		hdLog.LogE("Decode approval for all http request body failed! [%v]", err)
		setApprovalForAllOutput(&output, common.NewError(400))
		return
	}
	if !input.checkSign() {
		hdLog.LogE("Check input sign failed!")
		setApprovalForAllOutput(&output, common.NewError(401))
		return
	}
	tx, nftErr := trade.SetApprovalForAll(input.ReqChain, input.ContractAddr, input.CallAddr, input.ApprovedAddr, input.Flag)
	if !common.Valid(nftErr) {
		hdLog.LogE("Approve for all failed [%v]", nftErr.ErrorMsg)
		setApprovalForAllOutput(&output, nftErr)
		return
	}
	setApprovalForAllOutput(&output, common.NewError(200))
	output.TxHash = tx
	return
}
